package main

import (
	//"bytes"
	"flag"
	"fmt"
	"net"
	//"runtime"
	"encoding/binary"
	//"strings"
	"sync"
	//"sync/atomic"
	"runtime"
	"time"
)

const (
	mtuLimit       = 1500
	kcp_conv_begin = 1024 * 1024
)

var (
	xmitBuf sync.Pool
	s_port  string
)

func init() {
	xmitBuf.New = func() interface{} {
		return make([]byte, mtuLimit)
	}
}

type OurSess struct {
	conn          net.Conn // the underlying packet connection
	kcp           *KCP
	ch_read_event chan int
}

func currentMs() uint32 { return uint32(time.Now().UnixNano() / int64(time.Millisecond)) }

func gor_read(ses *OurSess) {
	buff2 := make([]byte, 512)

	for {
		n, err := ses.conn.Read(buff2)
		if nil != err {
			print(err.Error())
			return
		}
		if n > 0 {
			ses.kcp.Input(buff2[:n], true, true)
			ses.ch_read_event <- 1
		}
	}
}

func run_a_c(local_c int, ch chan int, tasks int) {
	result := -1
	defer func() {
		fmt.Println("defer", result)
		ch <- result
	}()

	udpconn, err := net.Dial("udp", s_port)
	if err != nil {
		return
	}
	defer udpconn.Close()

	sess := new(OurSess)
	sess.conn = udpconn

	sess.kcp = NewKCP(uint32(kcp_conv_begin+local_c), func(b []byte, l int) {
		udpconn.Write(b[:l])
	})
	sess.kcp.NoDelay(1, 33, 1, 1)

	sess.ch_read_event = make(chan int)

	go gor_read(sess)

	task_done := 0
	buff_send := make([]byte, 64)
	buff_read := make([]byte, 64)

	ticker := time.NewTicker(33 * time.Microsecond)
	defer ticker.Stop()

	for j := 0; j < tasks; j++ {
		binary.BigEndian.PutUint32(buff_send, uint32(j+1))

		sess.kcp.Send(buff_send[:4])

		for {
			select {
			case <-sess.ch_read_event:
			case <-ticker.C:
			}

			sess.kcp.Update()
			sess.kcp.flush(true)

			n := sess.kcp.Recv(buff_read)
			if n > 0 {
				break
			}
		}

		task_done += 1
		if task_done >= tasks {
			// all done
			break
		}
	}

	result = 0
}

func main() {
	count1 := 0
	count_success := 0

	concurent := flag.Int("c", 1, "concurent connections")
	times := flag.Int("n", 100, "number of tasks")
	pp := flag.String("p", ":23333", "host and port")

	flag.Parse()

	s_port = *pp

	runtime.GOMAXPROCS(4)

	a_chan := make(chan int, *concurent)

	time1 := time.Now()

	for i := 0; i < *concurent; i++ {
		count1 += 1
		go run_a_c(count1, a_chan, *times)
	}

	for {
		r1 := <-a_chan
		if 0 == r1 {
			count_success++
			fmt.Println("count_success", count_success)
			time2 := time.Now()
			fmt.Println("Time used:", time2.Sub(time1).String())

			if count_success >= *concurent {

				return
			}
		}
	}
}
